/**
 * 郭楠project
 * com.gn.commons
 * BaseMongo.java
 * 创建人:TonyGuo 
 * 时间：2016年12月11日-下午6:13:40 
 * 2016Tm公司-版权所有
 */
package com.gn.commons;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.DbCallback;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.Assert;

import com.taoxeo.data.base.AbstractBaseEntityDao;
import com.mongodb.DB;
import com.mongodb.MongoException;
import com.taoxeo.commons.core.dao.MongoSupport;
import com.taoxeo.data.base.Result;
import com.taoxeo.data.base.ResultCallback;
import com.taoxeo.data.exception.NotDeclaredIdException;
import com.taoxeo.data.exception.NullIdException;
import com.taoxeo.data.mongodb.MongoLifecycle;
import com.taoxeo.data.mongodb.MongoQueryHelper;
import com.taoxeo.lang.BeanReflectUtil;

/**
 * 
 * BaseMongo
 * 创建人:TonyGuo
 * 时间：2016年12月11日-下午6:13:40 
 * @version 1.0.0
 *  
 */
public class BaseMongo<T, ID extends Serializable> extends AbstractBaseEntityDao<T, ID>  {
	private static Logger logger = LoggerFactory.getLogger(BaseMongo.class);
	@Resource
	MongoSupport mongoSupport;
	
	/** The is mongo lifecycle. */
	protected boolean isMongoLifecycle = false;
	
	@Override
	public void save(final T entity) {
		logger.info("【article:{},{}】",mongoSupport,entity);
		if (getId(entity) == null) {
			logger.info("【enter...generateId...】");
			setId(entity, generateId());
		}
		mongoSupport.save(entity);
		mongoSupport.execute(new DbCallback<T>() {
			@Override
			public T doInDB(DB db) throws MongoException, DataAccessException {
				if (isMongoLifecycle) {
					if (((MongoLifecycle) entity).onSave(db)) {
						mongoSupport.save(entity);
					}
				}
				else {
					mongoSupport.save(entity);
				}
				return null;
			}
		});
		
	}
	
	/**
	 * <b>查询总记录数...</b>
	 * @since  1.0.0
	 */
	public long count(Query query) {
		return mongoSupport.count(query, entityClass);
	}
	
	/***
	 * <b>未实现...</b>
	 */
	@Override
	public void saveOrUpdate(T entity) {
		
	}
	
	/* (non-Javadoc)
	 * @see com.taoxeo.data.base.BaseEntityDao#update(java.lang.Object)
	 */
	@Override
	public void update(final T entity) {
		final ID id = getId(entity);
		if (id != null) {
			mongoSupport.execute(new DbCallback<T>() {
				public T doInDB(DB db) throws MongoException, DataAccessException {
					if (isMongoLifecycle) {
						if (((MongoLifecycle) entity).onUpdate(db)) {
							mongoSupport.dynamicUpdate(id, entity);
						}
					}
					else {
						mongoSupport.dynamicUpdate(id, entity);
					}
					return null;
				}
			});
		}
		else {
			throw new NullIdException();
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.taoxeo.data.base.BaseEntityDao#saves(java.util.List)
	 */
	@Override
	public void saves(List<T> entitys) {
		mongoSupport.insertAll(entitys);
	}
	

	/* (non-Javadoc)
	 * @see com.taoxeo.data.base.BaseEntityDao#delete(java.io.Serializable)
	 */
	@Override
	public void delete(final ID id) {
		mongoSupport.execute(new DbCallback<T>() {
			public T doInDB(DB db) throws MongoException, DataAccessException {
				if (isMongoLifecycle) {
					T entity = mongoSupport.findById(id, entityClass);
					if (entity != null) {
						if (((MongoLifecycle) entity).onDelete(db)) {
							mongoSupport.remove(entity);
						}
					}
				}
				else {
					mongoSupport.remove(MongoQueryHelper.queryID(id), entityClass);
				}
				return null;
			}
		});
	}


	/* (non-Javadoc)
	 * @see com.taoxeo.data.base.BaseEntityDao#get(java.io.Serializable)
	 */
	@Override
	public T get(ID id) {
		T t = mongoSupport.findById(id, entityClass);
		if (isMongoLifecycle) {
			((MongoLifecycle) t).onLoad(mongoSupport.getDb(), id);
		}
		return t;
	}

	/* (non-Javadoc)
	 * @see com.taoxeo.data.base.BaseEntityDao#gets(java.util.Collection)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<T> gets(Collection<ID> ids) {
		if (ids == null || ids.isEmpty()) {
			return Collections.EMPTY_LIST;
		}

		Query query = new Query(Criteria.where("_id").in(ids));
		return mongoSupport.find(query, entityClass);
	}

	

	
	/* (non-Javadoc)
	 * @see com.taoxeo.data.base.BaseEntityDao#findOne()
	 */
	@Override
	public T findOne() {
		return mongoSupport.findOne(new Query(), entityClass);
	}
	
	/**
	 * 简单分页查询.
	 *
	 * @param query 查询条件
	 * @param sort the sort
	 * @param skip 起始记录位置（跳过前多少记录数）
	 * @param size 分页大小
	 * @return the result
	 */
	protected Result<T> findResult(final Query query, final Sort sort, int skip, int size) {
		Result<T> result = new Result<T>(new ResultCallback<T>(skip, size) {
			public Long count() {
				return mongoSupport.count(query, entityClass);
			}

			public List<T> find() {
				if(size > 0){
					query.skip(skip).limit(size);
				}
				if(sort != null){
					query.with(sort);
				}
				return mongoSupport.find(query, entityClass);
			}
		});
		return result;
	}
	
	/**
	 * Find page.
	 *
	 * @param query the query
	 * @param pageable the page request
	 * @return the page
	 */
	protected Page<T> findPage(Query query, Pageable pageable, String... incluedFields){
		Assert.notNull(query, "Query not allow null");
		Assert.notNull(pageable, "Pageable not allow null");
		if(incluedFields.length > 0){
			Field field = query.fields();
			for(String key : incluedFields){
				field.include(key);
			}
		}
		Result<T> result = findResult(query, pageable.getSort(), pageable.getOffset(), pageable.getPageSize());
		PageImpl<T> page = new PageImpl<T>(result.getResultList(), pageable, result.getResultCount());
		return page;
	}
	
	/**
	 * Find
	 * 
	 * @param query
	 * @param incluedFields
	 * @return
	 */
	protected List<T> find(Query query, String... incluedFields){
		Assert.notNull(query, "Query not allow null");
		if(incluedFields.length > 0){
			Field field = query.fields();
			for(String key : incluedFields){
				field.include(key);
			}
		}
		return mongoSupport.find(query, entityClass);
	}
	
	
	/**
	 * Gets the mongoSupport.
	 * 
	 * @return the mongo support
	 */
	public MongoSupport getMongoSupport() {
		return mongoSupport;
	}

	/**
	 * Sets the mongoSupport.
	 * 
	 * @param mongoSupport the new mongo support
	 */
	public void setMongoSupport(MongoSupport mongoSupport) {
		this.mongoSupport = mongoSupport;
	}

	@SuppressWarnings("rawtypes")
	@Override
	protected void init() {
		// 获取主键字段
		PropertyDescriptor[] props = BeanReflectUtil.getProps(entityClass);
		for (PropertyDescriptor prop : props) {
			java.lang.reflect.Field field = FieldUtils.getField(entityClass, prop.getName(), true);
			if(field == null){
				continue;
			}
			if (field.getName().equals("id")) {
				idField = field;
			}
			if (field.getAnnotation(Id.class) != null) {
				idField = field;
				break;
			}
		}
		if (idField == null) {
			throw new NotDeclaredIdException(
					"Entity Class: "
							+ entityClass
							+ " must be declared id field: either declared a field named 'id' or a field annotated with @Id (org.springframework.data.annotation.Id)");
		}

		for (Class clazz : entityClass.getInterfaces()) {
			if (clazz == MongoLifecycle.class) {
				isMongoLifecycle = true;
				break;
			}
		}
	}

	


}
